/*
 * cxxomfort example: type_traits
 *
 * This program provides sample usage of the various
 * type_traits in C++, backported to the various revisions.
 */

#include <cxxomfort/cxxomfort.hpp>
#include <cxxomfort/library/type_name.hpp>
#include <cxxomfort/library/type_traits.hpp>
#include <cxxomfort/type_traits.hpp>
#include <cxxomfort/impl/11-is_constructible.hpp>
#include <type_traits>
#include <array>
#include <iostream>
#include <iomanip>
#include <string>
#include <deque>
#include <utility>

struct Foo {
    Foo (int, char) CXXO_NOEXCEPTNOTHROW {}
    Foo (int, wchar_t) {}
    template <typename T>
    explicit Foo (T) {}
    CXXO_CONSTEXPR Foo (Foo const&) CXXO_NOEXCEPTNOTHROW CXXOMFORT_CXX11_CODE(=default, {});
    virtual ~Foo() {}

    private:
};


typedef float T1;
typedef enum { value = 1 } T2;
//typedef std::ios_base::fmtflags T2;
typedef Foo T3;


template <typename T>
std::ostream& basic_traits(std::ostream& os) ;
template <typename T>
void cv_traits(std::ostream& os) ;
template <typename T>
void property_traits(std::ostream& os) ;
//template <typename T>
//void construction_traits(std::ostream& os) ;
//template <typename T>
//void supplemental_traits(std::ostream& os) ;


int main () {
    using namespace std;
    using cxxomfort::library::type_name;

    cxxomfort::output_info();
    cout<< endl;
    cout<< "T1 is: "<< type_name<T1>()<< endl;
    cout<< "T2 is: "<< type_name<T2>()<< endl;
    cout<< "T3 is: "<< type_name<T3>()<< endl;

    cout<< boolalpha<< endl;

    basic_traits<int>(cout);
    property_traits<int>(cout);
    //construction_traits<int>(cout);
    //supplemental_traits<int>(cout);
    cout<< "----\n"<< endl;

    // fill in a type here
    typedef  pair<const int, const bool> TT ;
    //
    basic_traits<TT>(cout);
    cv_traits<TT>(cout);
    property_traits<TT>(cout);
    //construction_traits<TT>(cout);
    //supplemental_traits<TT>(cout);
    cout<< "----\n"<< endl;

    cout<< "Info for Foo: "<< endl;
    basic_traits<Foo>(cout);
    property_traits<Foo>(cout);
    //construction_traits<Foo>(cout);
    //supplemental_traits<Foo>(cout);
    cout<< endl;


    cout<< endl;

#if 0
    cout<< type_name< CXXO_TYPEOF(declval<void>()) >()<< endl;
    cout<< type_name< CXXO_TYPEOF(declval<int>()) >()<< endl;

#if (CXXOMFORT_CXX_STD >= 2011)
    using ::cxxomfort::library::type_traits::is_specialization_of;

    typedef pair<string, bool> P;
    cout<< "pair ";
    cout<< is_specialization_of< P, pair >::value<< endl;
    cout<< is_specialization_of< string , pair >::value<< endl;
    cout<< "string ";
    cout<< is_specialization_of< P, basic_string >::value<< endl;
    cout<< is_specialization_of< string, basic_string>::value<< endl;

#else
    using ::cxxomfort::library::type_traits::detail::is_specialization_of_2;
    using ::cxxomfort::library::type_traits::detail::is_specialization_of_3;
    typedef pair<string, bool> P;
    cout<< "pair ";
    cout<< is_specialization_of_2< P, pair >::value<< endl;
    cout<< is_specialization_of_2< string , pair >::value<< endl;
    cout<< "string ";
    cout<< is_specialization_of_3< P, basic_string >::value<< endl; //< this one fails because P is smaller than basic_string
    cout<< is_specialization_of_3< string, basic_string>::value<< endl;
#endif

#endif

}

template <typename T>
std::ostream& basic_traits(std::ostream& os) {
    using namespace std;
    using cxxomfort::library::type_name;
    using cxxomfort::library::typeid_demangle;
    os<< "T : "<< type_name<T>()<< " is:"<< endl;
    os<< ""<< (is_integral<T>::value ? "integral\t" : "");
    os<< ""<< (is_floating_point<T>::value ? "floating\t" : "");
    os<< ""<< (is_arithmetic<T>::value ? "arithmetic\t" : "");
    os<< ""<< (is_null_pointer<T>::value ? "nullptr_t\t" : "");
    os<< ""<< (is_void<T>::value ? "void\t" : "");
    os<< " ; ";
    os<< ""<< (is_pointer<T>::value ? "pointer " : "");
    os<< ""<< (is_member_pointer<T>::value ? "member-pointer " : "");
    os<< ""<< (is_union<T>::value ? "union " : "");
    os<< ""<< (is_enum<T>::value ? "enum " : "");
    os<< ""<< (is_array<T>::value ? "array " : "");
    os<< ""<< (is_class<T>::value ? "class " : "");
    os<< " ; ";
    os<< ""<< (is_scalar<T>::value ? "scalar " : "");
    os<< ""<< (is_object<T>::value ? "object " : "");
    os<< ""<< (is_lvalue_reference<T>::value ? "lvalue-ref " : "");
    os<< ""<< (is_rvalue_reference<T>::value ? "rvalue-ref " : "");
    os<< ""<< (is_reference<T>::value ? "reference " : "");
    os<< ""<< (is_function<T>::value ? "function " : "");
    os<< " ; ";
    os<< ""<< (is_fundamental<T>::value ? "fundamental " : "");
    os<< ""<< (is_compound<T>::value ? "compound " : "");
    os<< endl;
    return os;
}

template <typename T>
void cv_traits(std::ostream& os) {
    using namespace std;
    using cxxomfort::library::type_name;
    os<< "T : cv-Qualifications: ";
    os<< ""<< (is_const<T>::value    ? "const " : "");
    os<< ""<< (is_volatile<T>::value ? "volatile " : "");
    os<< endl;
    return; // os<< endl;
}

template <typename T>
void property_traits(std::ostream& os) {
    using std::endl;
    using namespace std;
    using cxxomfort::library::type_name;
    os<< "T : Type Properties: "<< endl;
    os<< "decayed form: "<< cxxomfort::library::typeid_demangle( typeid(typename decay<T>::type) )<< flush<< '\n';
    os<< ""<< (is_empty<T>::value        ? "empty " : "");
    os<< ""<< (is_trivial<T>::value      ? "trivial " : "");
    os<< ""<< (is_pod<T>::value          ? "pod " : "");
    os<< ""<< (is_literal_type<T>::value ? "literal-type " : "");
    os<< ""<< (is_standard_layout<T>::value ? "standard-layout " : "");
    os<< " ; ";
    os<< ""<< (is_polymorphic<T>::value  ? "polymorphic " : "");
    os<< ""<< (is_abstract<T>::value     ? "abstract " : "");
    os<< ""<< (is_final<T>::value ? "final " : "");
    os<< endl;
    return; //os<< endl;
}


#if 0
template <typename T>
void construction_traits(std::ostream& os) {
    using namespace std;
    using cxxomfort::library::type_name;
    os<< "T : Construction: "<< endl;
    os<< "  Method        | triv   | nothr  | constexpr | member |\n"
      << " ---------------+--------+--------+-----------+--------+ "<< endl;
    os<< " def-ctor       | ";
    os<< setw(6)<< setfill('_')<< right<< (is_trivially_default_constructible<T>::value)<< "   ";
    os<< setw(6)<< setfill('_')<< right<< (is_nothrow_default_constructible<T>::value)<< "   ";
    os<< setw(9)<< setfill('_')<< right<< false<< "  ";
    os<< setw(6)<< setfill('_')<< right<< (is_default_constructible<T>::value)<< " ";
    os<< endl;
    os<< " copy-ctor      | ";
    os<< setw(6)<< setfill('_')<< right<< (is_trivially_copy_constructible<T>::value)<< "   ";
    os<< setw(6)<< setfill('_')<< right<< (is_nothrow_copy_constructible<T>::value)<< "   ";
    os<< setw(9)<< setfill('_')<< right<< false<< "  ";
    os<< setw(6)<< setfill('_')<< right<< (is_copy_constructible<T>::value)<< " ";
    os<< endl;
    os<< " move-ctor      | ";
    os<< setw(6)<< setfill('_')<< right<< (is_trivially_move_constructible<T>::value)<< "   ";
    os<< setw(6)<< setfill('_')<< right<< (is_nothrow_move_constructible<T>::value)<< "   ";
    os<< setw(9)<< setfill('_')<< right<< false<< "  ";
    os<< setw(6)<< setfill('_')<< right<< (is_move_constructible<T>::value)<< " ";

    os<< endl;
    os<< " destructor     | ";
    os<< setw(6)<< setfill('_')<< right<< (is_trivially_destructible<T>::value)<< "   ";
    os<< setw(6)<< setfill('_')<< right<< (is_nothrow_destructible<T>::value)<< "   ";
    os<< setw(9)<< setfill('_')<< right<< false<< "  ";
    os<< setw(6)<< setfill('_')<< right<< (is_destructible<T>::value)<< " ";
    //os<< (has_virtual_destructor<T>::value ? "~virtual ":"");

    os<< endl;
    os<< " ---------------+--------+--------+-----------+--------+ "<< endl;

    (void)os;
}
#endif // 0

#if 0
template <typename T>
void supplemental_traits(std::ostream& os) {
    using std::endl;
    using namespace std;
    using namespace cxxomfort::library::type_traits;
    os<< "T : Supplemental traits from cxxomfort: "<< endl;
    os<< ""<< (is_character_type<T>::value        ? "character-type " : "");
    os<< ""<< (is_std_byte<T>::value        ? "std::byte " : "");
    os<< ""<< (is_std_array<T>::value        ? "std::array " : "");
    os<< ""<< (is_std_pair<T>::value        ? "std::pair " : "");
    os<< endl;
    return; //os<< endl;
}
#endif
